const os = require('os');
const moment = require('moment');
const _ = require('lodash');
const OSS = require('ali-oss');

const config = include('/config');

const ossstore = new OSS({
  region: config.oss.region,
  accessKeyId: config.oss.id,
  accessKeySecret: config.oss.key,
  bucket: config.oss.bucket,
});

async function routes(fastify, options) {
  const db = fastify.db;
  const { date_format } = _.get(fastify, 'utils.constants');
  const ActionHelper = _.get(fastify, 'utils.ActionHelper');

  const getClientIp = function(request) {
    const forwardFor = _.get(request, 'headers.x-forwarded-for');
    const clientIp = _.get(request, 'raw.connection.remoteAddress');
    const ip = forwardFor || clientIp;
    return ip;
  };

  // 获取客户端的 ip
  function get_request_ip(request, reply, done) {
    const ip = getClientIp(request);
    request.ip = ip;
    done();
  }

  const validate_paopai_user = async function(request, reply) {
    const { action } = request;
    if (ActionHelper.isArenaSeasonReward(action)) {
      return true;
    }
    const userId = _.get(request, 'body.userId');
    const [users, fields] = await db.findOneUser(userId);
    if (users.length === 0) {
      console.log('用户不存在', userId);
      return reply.send({
        code: 0,
        code_str: `Success`,
      });
    }
    return true;
  };

  // 确认 IM 用户是否有效
  const validate_im_user = async function(request, reply) {
    const { action, ip } = request;
    const userId = _.get(request, 'body.userId');
    const selector = { _id: request.body.userId };
    let user = null;
    const options = {
      projection: { signupIp: 1, createdAt: 1, nickname: 1 },
    };
    if (ActionHelper.isRegister(action) && ip) {
      const update = { $set: { signupIp: ip } };
      user = await db.findOneAndUpdateUser(selector, update, options);
    } else {
      user = await db.findOneUser(selector, options);
    }
    request.user = user;
    if (!user) {
      console.log('用户不存在', userId);
      return reply.send({
        code: 0,
        code_str: `Success`,
      });
    }
    return true;
  };

  const validate_action = function(action_type) {
    return function validate_action(request, reply, done) {
      const action = _.get(request, 'body.action');
      request.action = action;
      if (ActionHelper.isValidUserAction(action, action_type)) {
        done();
      } else {
        console.log('action 无效', action);
        reply.send({ code: 0, code_str: 'Success' });
      }
    };
  };

  fastify.post('/statistics/paopai/v1/arena_statistics', {
    schema: {
      body: {
        type: 'object',
        required: ['action'], //, 'arena_statistics', 'userId'],
        properties: {
          action: { type: 'string' },
          arena_statistics: { type: 'string' },
          userId: { type: 'string' },

          winner_uid: { type: 'string' },
        },
      },
      response: {
        200: {
          type: 'object',
          properties: {
            code: { type: 'number' },
            code_str: { type: 'string' },
          },
        },
      },
    },
    beforeHandler: [validate_action(ActionHelper.game_type.paopai), get_request_ip, validate_paopai_user],
    handler: async (request, reply) => {
      const { action, userId, winner_uid, arena_statistics } = request.body;
      console.log(`收到跑牌竞技场数据 `, request.body);

      if (ActionHelper.isArenaRoomOver(action)) {
        const arena_action = JSON.parse(arena_statistics);
        const {
          season_id,
          season_name,
          arena_room_id,
          arena_room_rate,
          room_cost_gold,
          scores,
          golds,
          users,
        } = arena_action;

        const date = moment().format(date_format);
        const now = new Date();
        let [user_win_gold, user_lose_gold, ai_win_gold, ai_lose_gold, arena_room_cost_gold] = [0, 0, 0, 0, 0];
        const uids = _.keys(users);
        for (let uid of uids) {
          const user = users[uid];
          const { is_robot, nickname } = user;
          const gold = golds[uid];
          if (is_robot) {
            // 机器人只统计竞技场房间输赢
            if (gold > 0) {
              ai_win_gold += gold;
            } else {
              ai_lose_gold -= gold;
            }
          } else {
            arena_room_cost_gold += room_cost_gold;
            const selector = { uid: parseInt(uid), season_id, arena_room_id, date };
            // 玩家统计玩家输赢情况
            const changed_score = scores[uid];
            const win_gold = gold > 0 ? gold : 0;
            const lose_gold = gold < 0 ? -gold : 0;
            user_win_gold += win_gold;
            user_lose_gold += lose_gold;
            const update = {
              $set: { arena_room_rate, season_name, nickname, updated_at: now },
              $inc: { changed_score, played_rounds: 1, win_gold, lose_gold, room_cost_gold },
            };
            const { arena_users } = db;
            console.log(`收到跑牌竞技场数据 `, selector, update);
            await arena_users.updateArenaUser(selector, update);
          }
        }

        // 2. 赛季房间信息
        const { arena_rooms } = db;
        await arena_rooms.updateArenaRoom(
          { season_id, arena_room_id, date },
          {
            $set: { arena_room_rate, season_name, updated_at: now },
            $inc: {
              user_win_gold,
              user_lose_gold,
              ai_win_gold,
              ai_lose_gold,
              room_cost_gold: arena_room_cost_gold,
              played_rounds: 1,
            },
          }
        );

        // 3. 赛季统计
        const { arena_seasons } = db;
        await arena_seasons.updateArenaSeason(
          { season_id, date },
          {
            $set: { season_name, updated_at: now },
            $inc: {
              today_played_rounds: 1,
              today_user_win_gold: user_win_gold,
              today_user_lose_gold: user_lose_gold,
              today_ai_win_gold: ai_win_gold,
              today_ai_lose_gold: ai_lose_gold,
              today_room_cost_gold: arena_room_cost_gold,
            },
          }
        );
      } else if (ActionHelper.isArenaSeasonReward(action)) {
        const { rewards } = request.body;
        const season_rewards = JSON.parse(rewards);
        const { arena_seasons } = db;
        const { season_id } = season_rewards;
        await arena_seasons.updateArenaSeason({ season_id }, { $set: season_rewards }, { multi: true });

        console.log('赛季奖励', action);
      }

      reply.code(200).header('Content-Type', 'application/json; charset=utf-8');
      return { code: 0, code_str: 'success' };
    },
  });

  fastify.post('/statistics/paopai/v1/online_users', {
    schema: {
      body: {
        type: 'object',
        required: ['action', 'server'],
        properties: {
          // requried
          // 用户动作
          action: { type: 'string' }, // 用户动作 如 注册，打开 app, 活跃时间
          server: { type: 'string' }, // 服务器

          online_users: { type: 'number' }, // 在线人数

          uids: { type: 'string' },
          room_id: { type: 'string' },
          room_type_name: { type: 'string' },
          total_round: { type: 'number' },
        },
      },

      response: {
        200: {
          type: 'object',
          properties: {
            code: { type: 'number' },
            code_str: { type: 'string' },
          },
        },
      },
    },
    beforeHandler: [
      function validate_online_user_action(request, reply, done) {
        const action = _.get(request, 'body.action');
        const online_users = _.get(request, 'body.online_users');
        const server = _.get(request, 'body.server');
        const uids = JSON.parse(_.get(request, 'body.uids', '[]'));
        const room_id = _.get(request, 'body.room_id');
        const room_type_name = _.get(request, 'body.room_type_name');
        const total_round = _.get(request, 'body.total_round');
        if (ActionHelper.isOnlineUsers(action) && _.isNumber(online_users)) {
          request.action = action;
          done();
        } else if (
          ActionHelper.isGameOver(action) &&
          _.isArray(uids) &&
          _.isString(room_id) &&
          _.isString(room_type_name) &&
          _.isNumber(total_round)
        ) {
          request.uids = uids;
          request.action = action;
          done();
        } else {
          console.log('action或参数无效', action, request.body);
          reply.send({ code: 0, code_str: 'Success' });
        }
      },
      get_request_ip,
    ],
    handler: async (request, reply) => {
      console.log(`收到跑牌数据 `, request.body);
      const { action, ip } = request;
      const server = _.get(request, 'body.server');
      const now = moment();
      const date = now.format(date_format);
      const timestamp = now.valueOf();
      let result = null;
      let selector = null;
      let update = null;
      const from = '跑牌服务端';
      const online_users = _.get(request, 'body.online_users');

      if (ActionHelper.isOnlineUsers(action) && _.isNumber(online_users)) {
        selector = { date, server, action };
        update = { $set: { ...request.body, ip, timestamp, from } };
      } else if (ActionHelper.isGameOver(action)) {
        selector = { date, server, action, timestamp };
        update = { $set: { ...request.body, uids: request.uids, ip, from } };
      }

      result = await db.updateStatisticsAction(selector, update);

      if (_.get(result, 'result.ok') != 1) {
        console.log(`写入用户信息失败, `, result);
      } else {
      }

      reply.code(200).header('Content-Type', 'application/json; charset=utf-8');

      return { code: 0, code_str: 'success' };
    },
  });

  fastify.post('/statistics/paopai/v1/user', {
    schema: {
      body: {
        type: 'object',
        required: ['action', 'appId', 'device', 'deviceId', 'mobileOS', 'version', 'userId'],
        properties: {
          // requried
          // 用户动作
          action: { type: 'string' }, // 用户动作 如 注册，打开 app, 活跃时间

          // 用户设备信息
          appId: { type: 'string' }, // 统计的app id，目前为只有 IM
          device: { type: 'string' }, // 用户登陆的设备
          deviceId: { type: 'string' }, // 用户设备识别号
          mobileOS: { type: 'string' }, // 用户使用的系统
          version: { type: 'string' }, // 用户使用的 im 版本
          language: { type: 'string' }, // 语音版本
          os: { type: 'string' }, // 操作系统
          osVersion: { type: 'string' }, // 操作系统版本
          browserType: { type: 'string' }, // 浏览器类型
          browserVersion: { type: 'string' }, // 浏览器版本号

          // 用户信息
          userId: { type: 'number' }, // 用户 id
          created_at: { type: 'string' }, // 玩家注册时间

          // optional
          zipaiId: { type: 'number' }, // 字牌 id
          cost_cards: { type: 'number' }, // 消耗的房卡
          room_id: { type: 'string' }, // 房间号id
        },
      },

      response: {
        200: {
          type: 'object',
          properties: {
            code: { type: 'number' },
            code_str: { type: 'string' },
          },
        },
      },
    },

    beforeHandler: [validate_action(ActionHelper.game_type.paopai), get_request_ip, validate_paopai_user],
    handler: async (request, reply) => {
      console.log(`收到跑牌统计数据 `, request.body);
      const { action, ip } = request;
      const { userId } = request.body;
      const now = moment();
      const date = now.format(date_format);
      const timestamp = now.valueOf();
      let result = null;
      let selector = null;
      let update = null;
      let statistic_action = null;
      const from = '跑牌客户端';

      const createdAt = _.get(request, 'body.created_at');
      const userCreatedAt = moment(createdAt).format();

      if (ActionHelper.isRegister(action)) {
        statistic_action = { ...request.body, ip, date, timestamp, from, userCreatedAt };
        result = await db.insertStatisticsAction(statistic_action);
      } else if (ActionHelper.isOpenApp(action)) {
        selector = { date, userId, action };
        update = { $set: { ...request.body, ip, timestamp, from, userCreatedAt }, $inc: { openAppTimes: 1 } };
        result = await db.updateStatisticsAction(selector, update);
      } else if (ActionHelper.isPlayGame(action)) {
        selector = { date, userId, action };
        update = {
          $set: { ...request.body, ip, timestamp, from },
          $inc: { playGameTimes: 1 },
        };
        result = await db.updateStatisticsAction(selector, update);
      } else if (ActionHelper.isCostCards(action)) {
        selector = { date, userId, action };
        const body = _.omit(request.body, 'cost_cards');
        update = {
          $set: { ...body, ip, timestamp, from },
          $inc: { cost_cards: request.body.cost_cards },
        };
        result = await db.updateStatisticsAction(selector, update);
      }

      if (_.get(result, 'result.ok') != 1) {
        console.log(`写入用户信息失败, `, statistic_action, result);
      } else {
      }

      reply.code(200).header('Content-Type', 'application/json; charset=utf-8');

      return { code: 0, code_str: 'success' };
    },
  });

  fastify.post('/statistics/v1/user', {
    schema: {
      body: {
        type: 'object',
        required: ['action', 'appId', 'device', 'deviceId', 'mobileOS', 'version', 'userId', 'phone', 'userCreatedAt'],
        properties: {
          // requried
          // 用户动作
          action: { type: 'string' }, // 用户动作 如 注册，打开 app, 活跃时间

          // 用户设备信息
          appId: { type: 'string' }, // 统计的app id，目前为只有 IM
          device: { type: 'string' }, // 用户登陆的设备
          deviceId: { type: 'string' }, // 用户设备识别号
          mobileOS: { type: 'string' }, // 用户使用的系统
          version: { type: 'string' }, // 用户使用的 im 版本

          // 用户信息
          userId: { type: 'string' }, // 用户 id
          phone: { type: 'string' }, // 用户 手机号
          userCreatedAt: { type: 'string' }, // 用户注册时间

          // optional
          activeTime: { type: 'number' }, // 单次使用时长  - 以秒为单位 可选
        },
      },

      response: {
        200: {
          type: 'object',
          properties: {
            code: { type: 'number' },
            code_str: { type: 'string' },
          },
        },
      },
    },
    beforeHandler: [validate_action(ActionHelper.game_type.im), get_request_ip, validate_im_user],
    handler: async (request, reply) => {
      const { action, ip } = request;
      const { userId } = request.body;
      const now = moment();
      const date = now.format(date_format);
      const timestamp = now.valueOf();
      let result = null;
      let selector = null;
      let update = null;
      let statistic_action = null;
      const from = '小贰客户端';

      if (ActionHelper.isRegister(action)) {
        statistic_action = { ...request.body, ip, date, timestamp, from };
        result = await db.insertStatisticsAction(statistic_action);
      } else if (ActionHelper.isOpenApp(action)) {
        selector = { date, userId, action };
        update = { $set: { ...request.body, ip, timestamp, from }, $inc: { openAppTimes: 1 } };
        result = await db.updateStatisticsAction(selector, update);
      } else if (ActionHelper.isActiveTime(action)) {
        selector = { date, userId, action };
        update = {
          $set: { ...request.body, ip, timestamp, from },
          $push: { activeTimes: { seconds: request.body.activeTime || 0, at: now.format() } },
        };
        result = await db.updateStatisticsAction(selector, update);
      }

      if (_.get(result, 'result.ok') != 1) {
        console.log(`写入用户信息失败, `, statistic_action, result);
      } else {
      }

      reply.code(200).header('Content-Type', 'application/json; charset=utf-8');

      return { code: 0, code_str: 'success' };
    },
  });

  // TODO 加入限制 只能被服务端调用 不能被客户端调用
  fastify.post('/statistics/v1/payment', {
    // schema: {
    //   body: {
    //     type: 'object',
    //     required: ['action', 'money', 'userId'],
    //     properties: {
    //       action: { type: 'string' }, // 充值，提现，新建群组, 群红包, 群收款，群识别

    //       money: { type: 'number' }, // 充值，提现的金额 元为单位
    //       userId: { type: 'string' }, // 用户 id

    //       groupId: { type: 'string' }, // 群组 id
    //       groupName: { type: 'string' },
    //       orderId: { type: 'string' }, // 群收款，群红包，群识别
    //     },
    //   },
    //   response: {
    //     200: {
    //       type: 'object',
    //       properties: {
    //         code: { type: 'number' },
    //         code_str: { type: 'string' },
    //       },
    //     },
    //   },
    // },

    // beforeHandler: [
    //   function validate_payment_action(request, reply, done) {
    //     const action = _.get(request, 'body.action');
    //     if (ActionHelper.isValidPaymentAction(action)) {
    //       done();
    //     } else {
    //       reply.send({ code: 2, code_str: 'TODO payment action 参数错误' });
    //     }
    //   },
    //   get_request_ip,
    //   validate_im_user,
    // ],
    handler: async (request, reply) => {
      // const { action, ip } = request;
      // const now = moment();
      // const date = now.format(date_format);
      // const timestamp = now.valueOf();
      // const from = '小贰服务端';

      // const statistic_action = { ...request.body, ip, date, timestamp, from };
      // let result = await db.insertStatisticsAction(statistic_action);
      // if (_.get(result, 'result.ok') != 1) {
      //   console.log(`写入用户信息失败, `, statistic_action, result);
      // } else {
      // }

      reply.code(200).header('Content-Type', 'application/json; charset=utf-8');
      return { code: 0, code_str: 'success' };
    },
  });

  fastify.post('/upload_image', {
    schema: {
      body: {
        type: 'object',
        required: ['base64', 'name'],
        properties: {
          base64: { type: 'string' },
          name: { type: 'string' },
        },
      },
      response: {
        200: {
          type: 'object',
          properties: {
            code: { type: 'number' },
            code_str: { type: 'string' },
            image_url: { type: 'string' },
          },
        },
      },
    },
    handler: async (request, reply) => {
      const base64 = _.get(request, 'body.base64');
      const name = _.get(request, 'body.name');
      const filename =
        `paopai_shares/${moment().format('YYYYMMDD')}_${Math.random()}_${name}`.replace('.', '_') + '.png';

      console.log('上传图片', filename, base64.slice(0, 100));

      let image_url = '';
      let code = 1;
      let code_str = '上传图片失败';
      try {
        image_url = await ossstore
          .put(filename, Buffer.from(base64.replace('data:image/png;base64,', ''), 'base64'))
          .then(result => {
            return result.url;
          });
        code = 0;
        code_str = '上传图片成功';
      } catch (error) {
        console.log('上传图片失败', error);
      }
      return { code, code_str, image_url };
    },
  });

  const uptime = moment();
  fastify.get('/env', (request, reply) => {
    const freemem = os.freemem();
    const totalmem = os.totalmem();
    const usedmem = totalmem - freemem;
    const percentmem = usedmem * 100.0 / totalmem;
    const mem = `${(parseFloat(usedmem) / 1024 / 1024 / 1024).toFixed(3)}G/${(
      parseFloat(totalmem) /
      1024 /
      1024 /
      1024
    ).toFixed(3)}G(${percentmem.toFixed(2)}%)`;
    const cpus = os.cpus();
    _.each(cpus, cpu => {
      const { user, nice, sys, idle, irq } = cpu.times;
      cpu['使用率'] = `${(user * 100.0 / (user + nice + sys + idle + irq)).toFixed(2)}%`;
    });
    const perM = 1024 * 1024;
    const keyToValue = {
      rss: '常驻内存',
      heapTotal: '已申请的内存(堆)',
      heapUsed: '已使用的内存(堆)',
      external: '其他',
    };
    const json_env = {
      ['统计对象']: process.env.APP,
      ['统计环境']: process.env.CONFIG,
      ['cluster 实例']: process.env.INSTANCE_ID,

      ['Node 版本']: process.version,
      ['代码版本']: config.commit,

      ['系统内存使用率']: mem,
      ['app占用内存']: _.reduce(
        process.memoryUsage(),
        (s, v, k) => {
          s[keyToValue[k]] = `${(v / perM).toFixed(2)}M`;
          return s;
        },
        {}
      ),

      ['CPU']: cpus,
      ['启动时间']: `[${uptime.fromNow()}] ${uptime.format()}`,
    };

    const html = `<pre style="line-height:1.8;white-space:pre;font-family:monospace">${JSON.stringify(
      json_env,
      null,
      4
    )}</pre>`;
    reply
      .code(200)
      .header('Content-Type', 'text/html; charset=UTF-8')
      .send(html);
  });

  fastify.get('/analysis/:date', (request, reply) => {
    const db = _.get(fastify, 'db');
    const { date_format } = _.get(fastify, 'utils.constants');
    try {
      console.log('更新参数', request.params);
      const params = request.params;
      const date = moment(params.date).date(date_format);
      db.analysisActions(date);
    } catch (e) {
      console.log('更新错误', e);
    }

    reply.code(200).send();
  });

  // 响应负载均衡健康检查 // curl -I ip:port
  fastify.all('/', { logLevel: 'error' }, (request, reply) => {
    reply.code(200).send();
  });

  fastify.all('/*', (request, reply) => {
    reply.badRequest();
  });
}

module.exports = routes;
